package com.zmn.mcc.cas.server.boot.autoconfigure;

import com.zmn.mcc.cas.core.ShiroTokenManager;
import com.zmn.mcc.cas.server.CasServerStaffService;
import com.zmn.mcc.cas.server.ZmnStaffActiveHandler;
import com.zmn.mcc.cas.server.filter.ZmnFormAuthenticationFilter;
import com.zmn.mcc.cas.server.shiro.StaffShiroRealm;
import com.zmn.mcc.cas.web.ZmnShiroFilterFactoryBean;
import com.zmn.mcc.cache.DeptCache;
import com.zmn.mcc.cache.RoleCache;
import com.zmn.mcc.cache.StaffCache;
import com.zmn.mcc.manager.config.MccConfig;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authc.AbstractAuthenticator;
import org.apache.shiro.authc.AuthenticationListener;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;

import javax.servlet.Filter;
import java.util.*;

/**
 * @uthor nowind
 * @since 2018/9/10 17:56
 */
@Configuration
class CasServerShiroWebConfiguration {

    private final static String URL_INDEX = "/index.action";
    private final static String URL_LOGIN = "/login.action";
    private final static String URL_KICKOUT = "/kickout.action";
    private final static String URL_UNTITLED = "/untitled.action";

    @Value("${shiro.exclusionPaths:/static/**,/actuator/health}")
    private String exclusionPaths;

    @Autowired
    private CasServerProperties casServerProperties;

    /**
     * Shiro主过滤器
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) throws Exception {

        ZmnShiroFilterFactoryBean shiroFilter = new ZmnShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);

        // 配置未登录时拦截到的路径
        shiroFilter.setLoginUrl(URL_LOGIN);
        shiroFilter.setSuccessUrl(URL_INDEX);
        shiroFilter.setUnauthorizedUrl(URL_UNTITLED);

        //配置自定义过滤器
        Map<String, Filter> filters = new HashMap<>(16);
        ZmnFormAuthenticationFilter authenticationFilter = new ZmnFormAuthenticationFilter();
        if(StringUtils.isNotEmpty(casServerProperties.getKickoutUrl())){
            authenticationFilter.setKickoutUrl(casServerProperties.getKickoutUrl());
        }
        filters.put("zauthc", authenticationFilter);
        shiroFilter.setFilters(filters);

        LinkedHashMap<String, String> chainMap = new LinkedHashMap();
        chainMap.put("/captcha/**", "anon");
        chainMap.put("/actuator/**", "anon");
        chainMap.put("/static/**", "anon");
        chainMap.put("/*.ico", "anon");
        chainMap.put("/dwz/**", "anon");
        chainMap.put("/echarts/**", "anon");
        chainMap.put("/js/**", "anon");
        chainMap.put("/lay/**", "anon");
        chainMap.put("/WEB-INF/tlds/**", "anon");
        chainMap.put("/bind.action", "anon");
        chainMap.put("/cas/pwd.action", "anon");
        chainMap.put("/cas/updatePwd.action", "anon");
        chainMap.put("/index.action", "authc");
        chainMap.put("/test/**", "anon");
        chainMap.put("/demo/**", "anon");
        chainMap.put("/standard/record/preview.action", "anon");
        chainMap.put("/standard/**", "anon");
        chainMap.put("/common/uploadFile.action", "anon");

        chainMap.put("/cas/login.action", "anon");
        chainMap.put("/cas/tmpLogin.action", "anon");
        chainMap.put("/cas/logout.action", "anon");
        chainMap.put("/cas/bind.action", "anon");
        chainMap.put("/cas/authCodeLogin.action", "anon");

        chainMap.put("/login.action", "anon");
        chainMap.put("/tmpLogin.action", "anon");
        chainMap.put("/logout.action", "anon");
        chainMap.put("/kickout.action", "anon");
        chainMap.put("/untitled.action", "anon");
        chainMap.put("/authCodeLogin.action", "anon");
        chainMap.put("/jumpLogin.action", "anon");
        chainMap.put("/**", "zauthc");

        shiroFilter.setExclusionPaths(exclusionPaths);

        shiroFilter.setFilterChainDefinitionMap(chainMap);

        return shiroFilter;
    }

    /**
     * Realm实现(自定义)
     */
    @Bean
    public StaffShiroRealm staffShiroRealm(ShiroTokenManager shiroTokenManager, CasServerStaffService casServerStaffService, DeptCache deptCache, RoleCache roleCache, MccConfig config, StaffCache staffCache) {

        /*RedisAuthorizationCache redisAuthorizationCache*/

        StaffShiroRealm staffShiroRealm = new StaffShiroRealm(shiroTokenManager, casServerStaffService, deptCache, roleCache, config, staffCache);
        // 启用缓存，默认false
        staffShiroRealm.setCachingEnabled(true);
        // 启用身份验证缓存，即缓存AuthenticationInfo信息，默认false，启用缓存会导致单点登录由
        staffShiroRealm.setAuthenticationCachingEnabled(false);
        // 启用授权缓存，即缓存AuthorizationInfo信息，默认false
        staffShiroRealm.setAuthorizationCachingEnabled(true);
        // 缓存AuthorizationInfo信息的缓存名称
        staffShiroRealm.setAuthorizationCacheName("shiroAuthorizationCache");
        return staffShiroRealm;
    }

    /**
     * 安全管理器
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(StaffShiroRealm staffShiroRealm,
                                                     SessionManager sessionManager,
                                                     CacheManager shiroEhCacheManager, Collection<AuthenticationListener> authenticationListeners) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(staffShiroRealm);
        securityManager.setRememberMeManager(null);
        securityManager.setSessionManager(sessionManager);
        securityManager.setCacheManager(shiroEhCacheManager);
        ((AbstractAuthenticator) securityManager.getAuthenticator()).setAuthenticationListeners(authenticationListeners);
        return securityManager;
    }

    @Bean
    public Collection<AuthenticationListener> authenticationListeners(RedisTemplate redisTemplate) {
        ZmnStaffActiveHandler listener = new ZmnStaffActiveHandler();
        listener.setRedisTemplate(redisTemplate);
        ArrayList<AuthenticationListener> list = new ArrayList<>(1);
        list.add(listener);
        return list;
    }

    /**
     * 相当于调用SecurityUtils.setSecurityManager(securityManager)
     */
    @Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean(DefaultWebSecurityManager securityManager) throws Exception {
        MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
        factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        factoryBean.setArguments(securityManager);
        return factoryBean;
    }

    /**
     * Shiro Spring AOP权限注解的支持
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) throws Exception {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

}
